package org.agent.aigccore.basic.aop;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.agent.aigccore.core.service.log.LogService;
import org.agent.aigccore.model.po.OpsLogPO;
import org.agent.aigccore.model.po.UserInfoPO;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import org.agent.aigccore.basic.enums.LogLevel;
import org.agent.aigccore.basic.enums.LogType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.agent.aigccore.model.dto.LoginRequestDTO;
import org.agent.aigccore.model.dto.ChangePasswordRequestDTO;
import org.agent.aigccore.model.dto.ResetPasswordRequestDTO;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 操作日志切面处理器。
 * 基于 {@link OpLog} 注解与 {@link OpsEvent} 复合事件，统一处理日志记录。
 * 
 * 核心职责：
 * - 从 {@link org.agent.aigccore.basic.aop.spec.EventSpec} 提取模块/动作/类型/默认级别
 * - 支持注解级别覆盖默认级别
 * - 敏感字段自动脱敏（密码类参数）
 * - 统一写入 {@link OpsLogPO} 并落库
 * 
 * 设计特点：
 * - 完全消除魔法字符串，所有字段来源均为强类型枚举
 * - 保持单一入口注解，便于全局检索与治理
 * - 与 ops_log 宽表字段完全对齐
 */
@Aspect
@Component
public class OpLogAspect {

    @Resource
    private LogService logService;

    @Resource
    private ObjectMapper objectMapper;

    @Around("@annotation(org.agent.aigccore.basic.aop.OpLog)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Throwable thrown = null;
        Object result = null;
        try {
            result = pjp.proceed();
            return result;
        } catch (Throwable t) {
            thrown = t;
            throw t;
        } finally {
            long duration = System.currentTimeMillis() - start;
            OpsLogPO log = buildLog(pjp, result, thrown, duration);
            logService.offer(log);
        }
    }

    private OpsLogPO buildLog(ProceedingJoinPoint pjp, Object result, Throwable thrown, long duration) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = pjp.getTarget().getClass();

        OpLog annOnMethod = AnnotationUtils.findAnnotation(method, OpLog.class);
        OpLog annOnClass = AnnotationUtils.findAnnotation(targetClass, OpLog.class);

        OpsEvent event = null;
        if (annOnMethod != null) {
            event = annOnMethod.event();
        } else if (annOnClass != null) {
            event = annOnClass.event();
        }

        String module = targetClass.getSimpleName();
        String action = method.getName();
        String level = LogLevel.INFO.name();
        String logType = LogType.OPERATION.name();

        if (event != null) {
            var spec = event.getSpec();
            module = spec.module().name();
            action = spec.action().name();
            logType = spec.type().name();
            level = spec.defaultLevel().name();
        }

        if (annOnMethod != null) {
            level = annOnMethod.level().name();
        } else if (annOnClass != null) {
            level = annOnClass.level().name();
        }

        // 获取 HttpServletRequest
        HttpServletRequest request = null;
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                request = attributes.getRequest();
            }
        } catch (Exception e) {
            // 忽略异常，可能不是web请求
        }

        String ip = (request != null) ? request.getRemoteAddr() : null;
        String methodType = (request != null) ? request.getMethod() : null;
        String path = (request != null) ? request.getRequestURI() : null;

        Map<String, Object> extraMap = new HashMap<>();
        String[] paramNames = signature.getParameterNames();
        Object[] args = pjp.getArgs();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length && i < args.length; i++) {
                Object arg = args[i];
                // 敏感数据过滤
                if (arg instanceof LoginRequestDTO) {
                    LoginRequestDTO dto = (LoginRequestDTO) arg;
                    Map<String, Object> filteredDto = objectMapper.convertValue(dto, new TypeReference<Map<String, Object>>(){});
                    filteredDto.remove("password"); // 移除密码
                    extraMap.put(paramNames[i], filteredDto);
                } else if (arg instanceof ChangePasswordRequestDTO) {
                    ChangePasswordRequestDTO dto = (ChangePasswordRequestDTO) arg;
                    Map<String, Object> filteredDto = objectMapper.convertValue(dto, new TypeReference<Map<String, Object>>(){});
                    filteredDto.remove("oldPassword"); // 移除旧密码
                    filteredDto.remove("newPassword"); // 移除新密码
                    extraMap.put(paramNames[i], filteredDto);
                } else if (arg instanceof ResetPasswordRequestDTO) {
                    ResetPasswordRequestDTO dto = (ResetPasswordRequestDTO) arg;
                    Map<String, Object> filteredDto = objectMapper.convertValue(dto, new TypeReference<Map<String, Object>>(){});
                    filteredDto.remove("newPassword"); // 移除新密码
                    extraMap.put(paramNames[i], filteredDto);
                } else {
                    extraMap.put(paramNames[i], arg);
                }
            }
        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        String userId = null; // Change type to String
        if (auth != null && auth.isAuthenticated()) {
            if (auth.getPrincipal() instanceof UserInfoPO) {
                userId = ((UserInfoPO) auth.getPrincipal()).getUserId();
            } else {
                // 默认记录 username
                extraMap.put("username", auth.getName());
            }
        }

        String extraString;
        try {
            extraString = objectMapper.writeValueAsString(extraMap);
        } catch (Exception e) {
            extraString = "{}";
        }

        OpsLogPO log = new OpsLogPO();
        log.setTs(OffsetDateTime.now());
        log.setLogType(logType); // 设置日志类型
        log.setLevel(level);
        log.setApp("aigccore");
        log.setModule(module);
        log.setAction(action);
        log.setUserId(userId);
        log.setRequestId(UUID.randomUUID().toString());
        log.setDurationMs((int) duration);
        log.setSuccess(thrown == null);
        log.setStatusCode(thrown == null ? 200 : 500);
        log.setMessage(thrown == null ? "OK" : thrown.getMessage());
        log.setStack(thrown == null ? null : stackToString(thrown));
        log.setExtra(extraString);
        log.setIp(ip); // 设置 IP
        log.setMethod(methodType); // 设置 Method
        log.setPath(path); // 设置 Path
        return log;
    }

    private String stackToString(Throwable t) {
        StringBuilder sb = new StringBuilder();
        sb.append(t.toString()).append("\n");
        for (StackTraceElement e : t.getStackTrace()) {
            sb.append("\tat ").append(e.toString()).append("\n");
        }
        return sb.toString();
    }
}


